package ruleengine.admin.controller;

import static com.ruleengine.worker.runner.rule.RuleArgsField.DEST_RULE_BIFROMQ_TOPIC_KEY;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;

import java.util.List;
import java.util.Map;

import org.junit.jupiter.api.Test;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;

import com.ruleengine.admin.vo.CreateRulechainRequest;
import com.ruleengine.admin.vo.CreateRulechainResponse;
import com.ruleengine.admin.vo.DeleteRulechainRequest;
import com.ruleengine.admin.vo.Destination;
import com.ruleengine.admin.vo.GetRulechainResponse;
import com.ruleengine.admin.vo.RulechainOperationState;
import com.ruleengine.admin.vo.UpdateRulechainRequest;
import com.ruleengine.admin.vo.UpdateRulechainStateRequest;

import ruleengine.BaseTest;

/**
 * @author lianghanwei on 2024/3/25
 */
public class RuleChainControllerTest extends BaseTest {

    private static final String RULECHAIN_DESCRIPTION_UPDATED_CONTENT = "updatedRulechainDescription";

    @Test
    public void createRulechainWithIllegalName() {
        String rulechainId = createRulechain(DEMO_TENANT_ID, DEMO_INSTANCE_ID, "1");
        assertNull(rulechainId);
    }

    @Test
    public void create_updateConfig_updateState_delete_get() {
        String rulechainId = createRulechain(DEMO_TENANT_ID, DEMO_INSTANCE_ID);

        updateRulechain(DEMO_TENANT_ID, DEMO_INSTANCE_ID, rulechainId);
        assertEquals(RULECHAIN_DESCRIPTION_UPDATED_CONTENT, getRulechain(DEMO_TENANT_ID, DEMO_INSTANCE_ID, rulechainId).getDescription());

        // Can't delete ENABLED rulechain, so getRulechain name not null
        updateRulechainState(DEMO_TENANT_ID, DEMO_INSTANCE_ID, rulechainId, RulechainOperationState.ENABLED);
        deleteRulechain(DEMO_TENANT_ID, DEMO_INSTANCE_ID, rulechainId);
        assertNotNull(getRulechain(DEMO_TENANT_ID, DEMO_INSTANCE_ID, rulechainId).getName());

        // Can delete DISABLED rulechain, so getRulechain name null
        updateRulechainState(DEMO_TENANT_ID, DEMO_INSTANCE_ID, rulechainId, RulechainOperationState.DISABLED);
        deleteRulechain(DEMO_TENANT_ID, DEMO_INSTANCE_ID, rulechainId);
        assertNull(getRulechain(DEMO_TENANT_ID, DEMO_INSTANCE_ID, rulechainId).getName());
    }

    @Test
    public void sendMessageToDisabledRulechain() throws InterruptedException {
        String rulechainId = createRulechain(DEMO_TENANT_ID, DEMO_INSTANCE_ID);
        updateRulechain(DEMO_TENANT_ID, DEMO_INSTANCE_ID, rulechainId);
        updateRulechainState(DEMO_TENANT_ID, DEMO_INSTANCE_ID, rulechainId, RulechainOperationState.DISABLED);

        sendWorkerClient.send(buildDemoMessage(rulechainId));

        Thread.sleep(2000);
    }

    @Test
    public void sendMessageToEnabledRulechain() throws InterruptedException {
        String rulechainId = createRulechain(DEMO_TENANT_ID, DEMO_INSTANCE_ID);
        updateRulechain(DEMO_TENANT_ID, DEMO_INSTANCE_ID, rulechainId);
        updateRulechainState(DEMO_TENANT_ID, DEMO_INSTANCE_ID, rulechainId, RulechainOperationState.ENABLED);

        sendWorkerClient.send(buildDemoMessage(rulechainId));

        Thread.sleep(2000);
    }

    private String createRulechain(String tenantId, String instanceId) {
        return createRulechain(tenantId, instanceId, "newRulechainName");
    }

    private String createRulechain(String tenantId, String instanceId, String rulechainName) {
        CreateRulechainRequest request = new CreateRulechainRequest();
        request.setName(rulechainName);
        request.setDescription("newRulechainDescription");

        ResponseEntity<CreateRulechainResponse> createRulechainResp = restTemplate.postForEntity(String.format("%s/bifromq/%s/rulechain?tenantId=%s", path, instanceId, tenantId), request, CreateRulechainResponse.class);

        return createRulechainResp.getBody().getRulechainId();
    }

    private GetRulechainResponse getRulechain(String tenantId, String instanceId, String rulechainId) {
        return restTemplate.getForObject(String.format("%s/bifromq/%s/rulechain/%s?tenantId=%s", path, instanceId, rulechainId, tenantId), GetRulechainResponse.class);
    }

    private void deleteRulechain(String tenantId, String instanceId, String rulechainId) {
        DeleteRulechainRequest request = new DeleteRulechainRequest();
        request.setRulechainIds(List.of(rulechainId));

        restTemplate.exchange(String.format("%s/bifromq/%s/rulechain?tenantId=%s", path, instanceId, tenantId), HttpMethod.DELETE, new HttpEntity<>(request), Void.class);
    }

    private void updateRulechainState(String tenantId, String instanceId, String rulechainId, RulechainOperationState state) {
        UpdateRulechainStateRequest request = new UpdateRulechainStateRequest();
        request.setState(state);

        restTemplate.put(String.format("%s/bifromq/%s/rulechain/%s?tenantId=%s&&updateState", path, instanceId, rulechainId, tenantId), request);
    }

    private void updateRulechain(String tenantId, String instanceId, String rulechainId) {
        UpdateRulechainRequest request = new UpdateRulechainRequest();
        request.setDescription(RULECHAIN_DESCRIPTION_UPDATED_CONTENT);
        request.setTopic("updatedTopic");
        request.setFilterExpression(DEMO_FILTER_EXPRESSION);
        request.setMapExpression(DEMO_MAP_EXPRESSION);
        request.setDestinations(List.of(Destination.builder()
            .type(Destination.DestinationType.BIFROMQ)
            .args(Map.of(DEST_RULE_BIFROMQ_TOPIC_KEY, "bifromqMqttTopic"))
            .build())
        );

        restTemplate.put(String.format("%s/bifromq/%s/rulechain/%s?tenantId=%s", path, instanceId, rulechainId, tenantId), request);
    }

}
